package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.list.repository.GoodsRepository;
import com.atguigu.gmall.list.service.SearchService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseAttrInfo;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import io.netty.util.internal.StringUtil;
import lombok.SneakyThrows;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@SuppressWarnings("all")
public class SearchServiceImpl implements SearchService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ProductFeignClient productFeignClient;

    /**
     * 商品上架
     *
     * @param skuId
     */
    @Override
    public void upperGoods(Long skuId) {

        Goods goods = new Goods();

        //根据skuId获取skuInfo数据
        SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);

        //判断
        if (skuInfo != null) {
            goods.setId(skuId);
            goods.setDefaultImg(skuInfo.getSkuDefaultImg());
            goods.setTitle(skuInfo.getSkuName());
            goods.setPrice(skuInfo.getPrice().doubleValue());
            goods.setCreateTime(new Date());
            goods.setTmId(skuInfo.getTmId());

            //根据品牌查询品牌数据
            BaseTrademark trademark = productFeignClient.getTrademark(skuInfo.getTmId());
            //判断
            if (trademark != null) {

                goods.setTmName(trademark.getTmName());
                goods.setTmLogoUrl(trademark.getLogoUrl());

            }

            //根据三级分类id查询分类信息
            BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());
            //判断
            if (categoryView != null) {

                goods.setCategory1Id(categoryView.getCategory1Id());
                goods.setCategory1Name(categoryView.getCategory1Name());
                goods.setCategory2Id(categoryView.getCategory2Id());
                goods.setCategory2Name(categoryView.getCategory2Name());
                goods.setCategory3Id(categoryView.getCategory3Id());
                goods.setCategory3Name(categoryView.getCategory3Name());


            }


        }

        //根据skuId查询所属平台属性列表
        List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);
        //判断
        if (!CollectionUtils.isEmpty(attrList)) {

            List<SearchAttr> attrsList = attrList.stream().map(baseAttrInfo -> {

                //创建对象
                SearchAttr searchAttr = new SearchAttr();
                searchAttr.setAttrId(baseAttrInfo.getId());
                searchAttr.setAttrName(baseAttrInfo.getAttrName());
                searchAttr.setAttrValue(baseAttrInfo.getAttrValueList().get(0).getValueName());


                return searchAttr;
            }).collect(Collectors.toList());


            goods.setAttrs(attrsList);
        }


        goodsRepository.save(goods);

    }

    /**
     * 商品下架
     *
     * @param skuId
     */
    @Override
    public void lowerGoods(Long skuId) {
        goodsRepository.deleteById(skuId);
    }

    /**
     * 更新商品的热度排名
     * <p>
     * 思路：
     * 1.根据skuId获取当前商品的热度字段值
     * 2.自增
     * 3.修改当前商品的热度
     * <p>
     * 问题：
     * 1.自增是非原子性操作
     * redis--zset
     * 存储格式： hotScore   sku:21   1
     * sku:22   3
     * sku:23   2
     * <p>
     * 2.每次都去更新
     * 暂时将更新的结果存储到redis
     *
     * @param skuId
     */
    @Override
    public void incrHotScore(Long skuId) {

        String hotKey = "hotScore";
        //获取当前skuId热度
        Double score = redisTemplate.opsForZSet().incrementScore(hotKey, RedisConst.SKUKEY_PREFIX + skuId, 1);
        //redis统计10，更新一次es
        if (score % 10 == 0) {
            //获取skuId对应的商品
            Optional<Goods> optional = goodsRepository.findById(skuId);
            //判断
            if (optional.isPresent()) {

                Goods goods = optional.get();
                //设置热度
                goods.setHotScore(score.longValue());

                goodsRepository.save(goods);

            }

        }


    }

    @Autowired
    private RestHighLevelClient restHighLevelClient;


    /**
     * 搜索
     * <p>
     * 对接es:
     * 1、ElasticsearchRestTemplate
     * 2、GoodsRepository
     * 3、restHighLeveClient
     * https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.8/index.html
     */
    @Override
    @SneakyThrows
    public SearchResponseVo search(SearchParam searchParam) {

        //构建查询对象
        SearchRequest searchRequest = builderDsl(searchParam);
        //发送请求
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        //处理结果--需要根据es 返回的结果进行处理
        SearchResponseVo searchResponseVo=parsedReturnResult(searchResponse,searchParam);

        return searchResponseVo;
    }

    /**
     搜索结果处理

     */
    private SearchResponseVo parsedReturnResult(SearchResponse searchResponse,SearchParam searchParam){



        //创建最终结果对象
        SearchResponseVo responseVo=new SearchResponseVo();

        //获取聚合信息
        Map<String, Aggregation> aggregationMap = searchResponse.getAggregations().asMap();
        //获取品牌聚合
        ParsedLongTerms tmIdAggs = (ParsedLongTerms) aggregationMap.get("tmIdAggs");

        //获取数据
        List<SearchResponseTmVo> trademarkList = tmIdAggs.getBuckets().stream().map(bucket -> {

            //创建对象封装品牌

            SearchResponseTmVo searchResponseTmVo = new SearchResponseTmVo();

            //获取品牌id
            long tmId = ((Terms.Bucket) bucket).getKeyAsNumber().longValue();
            searchResponseTmVo.setTmId(tmId);

            //品牌聚合所有的子聚合
            Map<String, Aggregation> subAggrations = ((Terms.Bucket) bucket).getAggregations().asMap();
            //获取子聚合--品牌名称
            ParsedStringTerms tmNameAggs = (ParsedStringTerms) subAggrations.get("tmNameAggs");
            String tmName = tmNameAggs.getBuckets().get(0).getKeyAsString();
            searchResponseTmVo.setTmName(tmName);
            //获取子聚合--品牌logo
            ParsedStringTerms tmLogoUrlAggs = (ParsedStringTerms) subAggrations.get("tmLogoUrlAggs");
            String tmLogoUrl = tmLogoUrlAggs.getBuckets().get(0).getKeyAsString();
            searchResponseTmVo.setTmLogoUrl(tmLogoUrl);


            return searchResponseTmVo;
        }).collect(Collectors.toList());
        //添加品牌信息
        responseVo.setTrademarkList(trademarkList);

        //获取平台属性的聚合信息
        ParsedNested attrsAggs = (ParsedNested) aggregationMap.get("attrsAggs");
        //获取平台属性id聚合对象
        ParsedLongTerms attrIdAggs = (ParsedLongTerms) attrsAggs.getAggregations().getAsMap().get("attrIdAggs");
        //获取遍历具体数据
        List<SearchResponseAttrVo> attrsList = attrIdAggs.getBuckets().stream().map(bucket -> {

            //创建平台属性封装对象
            SearchResponseAttrVo responseAttrVo = new SearchResponseAttrVo();

            // 获取平台属性id
            long attrId = ((Terms.Bucket) bucket).getKeyAsNumber().longValue();
            responseAttrVo.setAttrId(attrId);
            //获取子聚合信息
            Map<String, Aggregation> subAttrsAggregation = ((Terms.Bucket) bucket).getAggregations().asMap();
            //获取属性名称
            ParsedStringTerms attrNameAggs = (ParsedStringTerms) subAttrsAggregation.get("attrNameAggs");
            //获取值
            String attrName = attrNameAggs.getBuckets().get(0
            ).getKeyAsString();
            responseAttrVo.setAttrName(attrName);

            //获取属性值
            ParsedStringTerms attrValueAggs = (ParsedStringTerms) subAttrsAggregation.get("attrValueAggs");
            //获取具体数据
            List<String> attrValueList = attrValueAggs.getBuckets().stream().map(subBucket -> {


                return ((Terms.Bucket) subBucket).getKeyAsString();
            }).collect(Collectors.toList());
            //设置属性值集合
            responseAttrVo.setAttrValueList(attrValueList);

            return responseAttrVo;
        }).collect(Collectors.toList());

        //设置平台属性
        responseVo.setAttrsList(attrsList);

        //获取商品列表数据
        SearchHits hits = searchResponse.getHits();
        SearchHit[] searchHits = hits.getHits();

        //创建集合封装数据
        List<Goods> goodsList = new ArrayList<>();
        //判断
        if(searchHits!=null&&searchHits.length>0){
            //遍历
            for (SearchHit searchHit : searchHits) {

                //转换数据
                Goods goods = JSONObject.parseObject(searchHit.getSourceAsString(), Goods.class);

                //获取高亮数据
                if(searchHit.getHighlightFields().get("title")!=null){


                    goods.setTitle(searchHit.getHighlightFields().get("title").getFragments()[0].toString());
                }


                goodsList.add(goods);

            }
        }
        responseVo.setGoodsList(goodsList);


        //设置总记录数据
        long total = hits.getTotalHits().value;
        responseVo.setTotal(total);

        responseVo.setPageNo(searchParam.getPageNo());
        responseVo.setPageSize(searchParam.getPageSize());
        //计算总页数 10  3  4  9  3  3
        if(total%searchParam.getPageSize()==0){

            responseVo.setTotalPages(total/searchParam.getPageSize());
        }else{
            responseVo.setTotalPages((total/searchParam.getPageSize())+1);
        }


        return responseVo;
    }



    /**
     * 构建dsl条件，封装请求对象
     */
    private SearchRequest builderDsl(SearchParam searchParam) {

        // 创建搜索请求对象
        SearchRequest searchRequest = new SearchRequest("goods");
        //构建请求参数
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //创建多条件查询对象
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();


        //添加关键字--keyword
        //判断
        if (!StringUtils.isEmpty(searchParam.getKeyword())) {


            boolQuery.must(
                    QueryBuilders.matchQuery("title", searchParam.getKeyword()).operator(Operator.AND)
            );
        }


        //添加三级分类
        if (searchParam.getCategory3Id() != null) {


            boolQuery.filter(QueryBuilders.termQuery("category3Id", searchParam.getCategory3Id()));
        }

        //添加三级分类
        if (searchParam.getCategory2Id() != null) {


            boolQuery.filter(QueryBuilders.termQuery("category2Id", searchParam.getCategory2Id()));
        }
        //添加三级分类
        if (searchParam.getCategory1Id() != null) {


            boolQuery.filter(QueryBuilders.termQuery("category1Id", searchParam.getCategory1Id()));
        }


        //品牌条件添加
        String trademark = searchParam.getTrademark();
        //2:华为
        if (!StringUtils.isEmpty(trademark)) {

            String[] split = trademark.split(":");
            //判断
            if (split != null && split.length == 2) {

                boolQuery.filter(QueryBuilders.termQuery("tmId", split[0]));
            }

        }


        //平台属性
        String[] props = searchParam.getProps();
        //判断
        //props=23:4G:运行内存
        // &props=24:128G:机身内存
        if (props != null && props.length > 0) {
            for (String prop : props) {
                //prop:23:4G:运行内存
                String[] split = prop.split(":");
                //判断
                if (split != null && split.length == 3) {


                    //构建外层bool
                    BoolQueryBuilder attrBoolQuery = QueryBuilders.boolQuery();

                    //构建子bool
                    BoolQueryBuilder subAttrBoolQuery = QueryBuilders.boolQuery();

                    subAttrBoolQuery.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                    subAttrBoolQuery.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));

                    attrBoolQuery.must(QueryBuilders.nestedQuery("attrs", subAttrBoolQuery, ScoreMode.None));


                    boolQuery.filter(attrBoolQuery);


                }


            }


        }


        //添加匹配条件
        searchSourceBuilder.query(boolQuery);


        //品牌聚合
        TermsAggregationBuilder tmIdAggs = AggregationBuilders.terms("tmIdAggs").field("tmId");

        //子聚合
        tmIdAggs.subAggregation(AggregationBuilders.terms("tmNameAggs").field("tmName"));
        tmIdAggs.subAggregation(AggregationBuilders.terms("tmLogoUrlAggs").field("tmLogoUrl"));

        //条件对象添加聚合信息
        searchSourceBuilder.aggregation(tmIdAggs);


        //聚合平台属性
        searchSourceBuilder.aggregation(AggregationBuilders.nested("attrsAggs", "attrs")
                .subAggregation(AggregationBuilders.terms("attrIdAggs").field("attrs.attrId")
                        .subAggregation(AggregationBuilders.terms("attrNameAggs").field("attrs.attrName"))
                        .subAggregation(AggregationBuilders.terms("attrValueAggs").field("attrs.attrValue"))));


        //添加排序规则
        //获取参数中的排序规则和字段
        //order=2:desc
        //1:hotScore 2:price
        String order = searchParam.getOrder();
        //判断
        if (!StringUtils.isEmpty(order)) {

            //截取数据
            String[] split = order.split(":");
            //判断
            if (split != null && split.length == 2) {

                String filed = "";
                //order=2:desc
                switch (split[0]) {

                    case "1":
                        filed = "hotScore";
                        break;
                    case "2":
                        filed = "price";
                        break;
                }

                //设置排序规则
                searchSourceBuilder.sort(filed, "desc".equals(split[1]) ? SortOrder.DESC : SortOrder.ASC);


            }

        } else {
            //说明页面没有选择排序方式
            searchSourceBuilder.sort("hotScore", SortOrder.DESC);

        }


        //设置高亮
        HighlightBuilder builder = new HighlightBuilder();
        builder.preTags("<font color='red'>");
        builder.postTags("</font>");
        builder.field("title");


        searchSourceBuilder.highlighter(builder);


        //设置分页
        //计算开始索引

        int index = (searchParam.getPageNo() - 1) * searchParam.getPageSize();
        //设置开始索引
        searchSourceBuilder.from(index);
        //设置每页条数
        searchSourceBuilder.size(searchParam.getPageSize());


        //结果集处理 "id","defaultImg","title","price"

        searchSourceBuilder.fetchSource(new String[]{"id", "defaultImg", "title", "price"}, null);

        //输出打印dsl语句，查看是否完整

        System.out.println(searchSourceBuilder.toString());
        //添加构建的条件到请求对象
        searchRequest.source(searchSourceBuilder);
        return searchRequest;
    }

}